﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using FishSimulation.Model;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using Image = System.Drawing.Image;

namespace FishSimulation {
    public static class Utils {
        public static ScriptEngine pyEngine = null;
        public static ScriptRuntime pyRuntime = null;
        public static ScriptScope pyScope = null;

        static Random Random = new Random();

        public static string ImagePath {
            get;
            private set;
        }

        public static string Path {
            get;
            private set;
        }
        
        static Utils() {
            Path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            ImagePath = System.IO.Path.Combine(Path, "Images");
            pyEngine = Python.CreateEngine();
            pyScope = pyEngine.CreateScope();
        }

        /// <summary>
        /// Converts a base 64 string to an image
        /// </summary>
        /// <param name="base64String">The base64 string.</param>
        /// <returns></returns>
        public static Image Base64ToImage(string base64String) {
            // Convert Base64 String to byte[]
            byte[] imageBytes = Convert.FromBase64String(base64String);
            System.IO.MemoryStream ms = new System.IO.MemoryStream(imageBytes, 0,
              imageBytes.Length);

            // Convert byte[] to Image
            ms.Write(imageBytes, 0, imageBytes.Length);
            Image image = Image.FromStream(ms, true);
            return image;
        }

        public static Image ByteArrayToImage(byte[] byteArrayIn) {
            MemoryStream ms = new MemoryStream(byteArrayIn);
            Image returnImage = Image.FromStream(ms);
            return returnImage;
        }

        public static bool CompareString(this string a, string b) {
            var x1 = a == null ? "" : a.ToLower().Trim();
            var x2 = b == null ? "" : b.ToLower().Trim();
            return x1.CompareTo(x2) == 0;
        }

        public static bool Confirm(this Window c, string msg)
        {
            return MessageBox.Show(c, msg, "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
        }

        public static bool Confirm(this Control c, string msg)
        {           
            return MessageBox.Show(msg, "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
        }

        public static Window CreateWindow() {
            Window w = new Window();
            w.Height = 500;
            w.Width = 800;
            return w;
        }

        public static double Distance(double x1, double y1, double x2, double y2) {
            double dis = 0;

            double a = (x2 - x1) * (x2 - x1);
            double b = (y2 - y1) * (y2 - y1);
            double c = a + b;
            dis = Math.Sqrt(c);

            return dis;
        }

        public static string Formatt(this string template, params object[] args) {
            return String.Format(template, args);
        }

        public static string Join(this IEnumerable<object> obj, string str) { 
            string result = "";

            bool first = true;
            foreach (object o in obj) {
                if (first)
                {
                    first = false;
                    result = String.Format("{0}", o);
                }
                else {
                    result += String.Format(",{0}", o);
                }
            }
            
            return result;
        }

        public static Rect GetBoundingBox(Cord p1, Cord p2, Cord p3) { 
            //bbox[x1] = min(triangles[1][x], triangles[2][x], triangles[3][x])
            //bbox[x2] = max(triangles[1][x], triangles[2][x], triangles[3][x])
            //bbox[y1] = min(triangles[1][y], triangles[2][y], triangles[3][y])
            //bbox[y2] = max(triangles[1][y], triangles[2][y], triangles[3][y])

            int x1 = Min((int)p1.X, (int)p2.X, (int)p3.X);
            int x2 = Max((int)p1.X, (int)p2.X, (int)p3.X);
            int y1 = Min((int)p1.Y, (int)p2.Y, (int)p3.Y);
            int y2 = Max((int)p1.Y, (int)p2.Y, (int)p3.Y);

            int width = 0;
            if(x1 > x2)
                width = x1 - x2;
            else
                width = x2 - x1;

            int height = 0;
            if(y1 > y2)
                height = y1 - y2;
            else
                height = y2 - y1;

            return new Rect(x1, y1, width, height);
        }

        public static Image GetImage(string name) {
            Image i = null;
            string path = System.IO.Path.Combine(Utils.ImagePath, name);
            if (System.IO.File.Exists(path)) { 
                i = Image.FromFile(path);
            }
            return i;
        }

        /// <summary>
        /// Converts an image to a base 64 string
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="format">The format.</param>
        /// <returns></returns>
        public static string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format) {
            string base64String = null;
            using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
                // Convert Image to byte[]
                image.Save(ms, format);
                byte[] imageBytes = ms.ToArray();

                // Convert byte[] to Base64 String
                base64String = Convert.ToBase64String(imageBytes);                
            }
            return base64String;
        }

        public static byte[] ImageToByteArray(Image imageIn) {
            MemoryStream ms = new MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
            return ms.ToArray();
        }

        public static System.Windows.Media.ImageSource ImageToMediaImage(System.Drawing.Image image) {
            // Winforms Image we want to get the WPF Image from...
            var bitmap = new System.Windows.Media.Imaging.BitmapImage();
            bitmap.BeginInit();
            MemoryStream memoryStream = new MemoryStream();
            // Save to a memory stream...
            image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            // Rewind the stream...
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
            bitmap.StreamSource = memoryStream;
            bitmap.EndInit();
            return bitmap;
        }

        public static int Max(params int[] arr) {
            int max = Int32.MinValue;
            foreach (int i in arr) {
                if (i > max) {
                    max = i;
                }
            }
            return max;
        }

        public static int Min(params int[] arr) {
            int min = Int32.MaxValue;
            foreach (int i in arr) {
                if (i < min) {
                    min = i;
                }
            }
            return min;
        }

        public static bool RandomBoolean() {
            return Convert.ToBoolean(Utils.RandomInt(0, 1));
        }

        /// <summary>
        /// Gets a random cord from the given rectangle
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public static Cord RandomCord(Rect rect) {
            int x = Utils.RandomInt((int)rect.X, (int)rect.X + (int)rect.Width);
            int y = Utils.RandomInt((int)rect.Y, (int)rect.Y + (int)rect.Height);
            return new Cord(x, y);
        }

        /// <summary>
        /// Gets a random date that is a maximum number of days in the past
        /// </summary>
        /// <param name="maxNumberOfDays"></param>
        /// <returns></returns>
        public static DateTime RandomDateInThePast(int maxNumberOfDays) {
            return RandomDate(DateTime.Now.AddDays(-1 * maxNumberOfDays), DateTime.Now);
        }

        public static DateTime RandomDate(DateTime? minDate, DateTime? maxDate) {
            DateTime min = minDate ?? DateTime.MinValue;
            DateTime max = maxDate ?? DateTime.MaxValue;
            int year = Random.Next(min.Year, max.Year);
            
            int month = 0;
            if (year == max.Year)
                month = Random.Next(min.Month, max.Month);
            else
                month = Random.Next(1, 13);

            int day = 0;
            if (year == max.Year && month == max.Month)
                day = Random.Next(min.Day, max.Day);
            else
                day = Random.Next(1, DateTime.DaysInMonth(year, month) + 1);
            
            return new DateTime(year, month, day);
        }

        public static int RandomInt(int min, int max) {
            return Random.Next(min, max + 1);
        }

        public static long RandomInt64(long min, long max)
        {
            return (long)Random.Next((int)min, (int)max + 1);
        }

        /// <summary>
        /// Resizes an image
        /// </summary>
        /// <param name="image">image object to resize</param>
        /// <param name="targetSize">a target size to shoot for when resizing the image</param>
        /// <param name="keepProportion">
        /// should the image be kept proportionate to the origional image, or should the target
        /// size be used no matter what
        /// </param>
        /// <returns>Resized image object</returns>
        public static Image ResizeImage(System.Drawing.Image image, System.Drawing.Size targetSize, bool keepProportion) {
            //if keep proportion is true attempt to resize the image and keep the height to width ratio.
            //else resize the image to the exact target size and return it
            if (keepProportion) {
                //make sure the image is larger than the targetSize
                if (image.Height > targetSize.Height || image.Width > targetSize.Width) {
                    float heightRatio = (float)targetSize.Height / image.Height;
                    float widthRatio = (float)targetSize.Width / image.Width;

                    if (image.Height >= image.Width) {
                        targetSize.Height = (int)(image.Height * heightRatio);
                        targetSize.Width = (int)(image.Width * heightRatio);
                    }
                    else if (image.Width > image.Height) {
                        targetSize.Height = (int)(image.Height * widthRatio);
                        targetSize.Width = (int)(image.Width * widthRatio);
                    }
                }
                else {
                    //image was smaller than the target size, but the user wanted 
                    //to keep the proportions, so just return the image as is
                    targetSize.Height = image.Height;
                    targetSize.Width = image.Width;
                }
            }

            return new Bitmap(image, targetSize); ;
        }

        public static bool? TryParseBool(this object o) {
            bool? result = null;
            if(o != null) {
                bool temp = false;
                if (Boolean.TryParse(o.ToString(), out temp))
                    result = temp;
            }
            return result;
        }

        public static DateTime? TryParseDateTime(this object o) {
            DateTime? result = null;
            if(o != null) {
                DateTime temp = DateTime.MinValue;
                if (DateTime.TryParse(o.ToString(), out temp))
                    result = temp;
            }
            return result;
        }

        public static int? TryParseInt(this object o) {
            int? result = null;
            if(o != null) {
                int temp = 0;
                if (Int32.TryParse(o.ToString(), out temp))
                    result = temp;
            }
            return result;
        }

        public static double? TryParseDouble(this object o) {
            double? result = null;
            if(o != null) {
                double temp = 0;
                if (Double.TryParse(o.ToString(), out temp))
                    result = temp;
            }
            return result;
        }

        public static long? TryParseInt64(this object o) {
            long? result = null;
            if(o != null) {
                long temp = 0;
                if (Int64.TryParse(o.ToString(), out temp))
                    result = temp;
            }
            return result;
        }
    }
}
